package file

import (
    "os"
    "syscall"
    "crypto/md5"
    "exec"
    "io/ioutil"
    "fmt"
)
const (
    _Bufsize = 1024 * 4 // 4 KBs
    _Hashsize = 16   //size of a hash in bytes
)

type File struct {
    fd int      //file descriptor
    name string // file name
    md5 string
}

func newFile(fd int, name string) *File{
    if fd < 0{
        return nil
    }
    return &File{fd, name, ""} 
}

var (
    Stdin = newFile(0, "/dev/stdin")
    Stdout = newFile(1, "dev/stdout")
    Stderr = newFile(2, "dev/stderr")
)

func Open(name string, mode int, perm int) (file *File, err os.Error){
   r, e := syscall.Open(name, mode, perm)
   if e != 0{
       err = os.Errno(e)
   }
   return newFile(r, name), err
}

func (file *File) Close() os.Error{
    if file == nil{
        return os.EINVAL
    }
    e := syscall.Close(file.fd)
    file.fd = -1 //so we can't reclose it
    if e!= 0{
        return os.Errno(e)
    }
    return nil
}

func (file *File) Read(b []byte)(ret int, err os.Error){
    if file == nil{
        return -1, os.EINVAL
    }
    r, e := syscall.Read(file.fd, b)
    if e != 0{
        err = os.Errno(e)
    }
    return int(r), err
}

func (file *File) Write(b []byte) (ret int, err os.Error){
    if file == nil{
        return -1, os.EINVAL
    }
    r, e := syscall.Write(file.fd, b)
    if e != 0{
        err = os.Errno(e)
    }
    return int(r), err
}

func (file *File) String() string {
    return file.name
}
/*
/ Calculate the MD5 hash of this file
*/
func (file *File) CalcMD5(force_internal bool) {//([]byte, os.Error){
    if file == nil{
        return
    }
    //use external md5 app if it exists
    cmdpath, err := exec.LookPath("md5")
    if err == nil && !force_internal{
        cmd, err := exec.Run(cmdpath, []string{"-q", file.String()}, nil, exec.DevNull, exec.Pipe, exec.PassThrough)
        if err != nil{
            fmt.Printf("error running command: %s\n", err.String())
            return
        }   
        cmd.Wait(0)
        hash, err := ioutil.ReadAll(cmd.Stdout)
        if err != nil{
            fmt.Printf("error reading command output: %s\n", err.String())
            return
        }
        //fuckin thing sucks - apparently "quiet" doesn't mean what it says it does
        file.md5 = fmt.Sprintf("%s", hash[19:])
    //use Go's md5 implementation (it's slower...)
    }else{
        rawdata := make([]byte, _Bufsize)
        digest := md5.New()
        nbytes, err := file.Read(rawdata)
        for nbytes > 0 && err == nil{
            //write the data to the digest _Bufsize bytes at a time
            digest.Write(rawdata[0:nbytes])
            nbytes, err = file.Read(rawdata)
        }
        //compute the hash
        file.md5 =  fmt.Sprintf("%x", digest.Sum())
    }
}
func (file *File) MD5() string{
    return file.md5
}




















